package ua.edu.nuos.jeetraining2012.cms.servlet;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ua.edu.nuos.jeetraining2012.cms.entity.UserEntity;
import ua.edu.nuos.jeetraining2012.cms.service.Course;
import ua.edu.nuos.jeetraining2012.cms.service.exception.*;
import ua.edu.nuos.jeetraining2012.cms.util.LocaleWrapper;
import ua.edu.nuos.jeetraining2012.cms.util.interceptor.ShiroSecurity;
import ua.edu.nuos.jeetraining2012.cms.util.mail.Mail;
import ua.edu.nuos.jeetraining2012.cms.util.servlet.CmsServlet;
import ua.edu.nuos.jeetraining2012.cms.util.template.TemplateRenderer;
import ua.edu.nuos.jeetraining2012.cms.util.template.course.IndexTemplate;
import ua.edu.nuos.jeetraining2012.cms.util.template.course.MyCoursesTemplate;
import ua.edu.nuos.jeetraining2012.cms.util.template.material.SVNTemplate;
import ua.edu.nuos.jeetraining2012.cms.util.template.layout.DefaultLayoutTemplate;

import javax.ejb.EJB;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ResourceBundle;


/**
 * Course controller servlet.
 * <p/>
 * Available actions:
 * <ul>
 * <li>index &mdash; list of all available courses.</li>
 * <li>view &mdash; show detailed info about selected course.</li>
 * </ul>
 *
 * @author Victor N. Ganichev victor.ganichev@gmail.com
 * @version 1.0 Date: 23.04.13 Time: 22:21
 * @since 1.0
 */
@ShiroSecurity
@WebServlet(urlPatterns = {"/course", "/course/*"})
public class CourseServlet extends CmsServlet {

    /** Inject Mail */
    @EJB
    protected Mail mail;

    /** Define logger. */
    final static private Logger logger = LoggerFactory.getLogger(CourseServlet.class);

    /** Define current controller */
    final private static String CURRENT_CONTROLLER = "course";

    /**
     * Inject BL
     */
    @EJB
    Course course;

    // Constructors.
    public CourseServlet() {
        logger.debug("CourseServlet - invocation.");
    }

    /**
     * Index action.
     * <p/>
     * Show all available courses.
     *
     * @param request  HttpServletRequest
     * @param response HttpServletResponse
     */
    public void indexAction(HttpServletRequest request, HttpServletResponse response) throws IOException {
        logger.debug("indexAction() - started.");
        final String CURRENT_ACTION = "index";

        TemplateRenderer renderer = new TemplateRenderer();
        DefaultLayoutTemplate template = new IndexTemplate();
        template.setContextPath(request.getContextPath());
        template.setCurrentAction(CURRENT_ACTION);
        template.setCurrentController(CURRENT_CONTROLLER);

        if (!renderer.process(template, response.getWriter())) {
            try {
                request.getRequestDispatcher("/error.jspx").forward(request, response);
            } catch (ServletException e) {
                logger.error("loginAction() - Can't forward to error.jspx", e);
            }
        }

        logger.debug("indexAction() - finished.");
    }

    /**
     * View action.
     * <p/>
     * Show course details.
     *
     * @param request  HttpServletRequest user request.
     * @param response HttpServletResponse server response.
     * @throws IOException Will thrown if can't get writer or this exception produced by
     *                     {@link HttpServletRequest#getRequestDispatcher(String)} method.
     */
    public void viewAction(HttpServletRequest request, HttpServletResponse response) throws IOException {
        logger.debug("viewAction() - started.");
        try {
            request.getRequestDispatcher("/error.jspx").forward(request, response);
        } catch (ServletException e) {
            logger.error("loginAction() - Can't forward to error.jspx", e);
        }
        logger.debug("viewAction() - finished.");
    }

    /**
     * Show list of user subscribed courses.
     * If user unauthenticated - redirect him to course list page.
     *
     * @param request           HttpServletRequest user request.
     * @param response HttpServletResponse server response.
     * @throws IOException Will thrown if can't get writer or this exception produced by
     *                     {@link HttpServletRequest#getRequestDispatcher(String)} method.
     */
    public void mycoursesAction(HttpServletRequest request, HttpServletResponse response) throws IOException{
        logger.debug("mycoursesAction() - started.");

        final String CURRENT_ACTION = "mycourses";
        final Subject subject = SecurityUtils.getSubject();
        if (subject == null || !subject.isAuthenticated()) {
            logger.debug("mycoursesAction() - Un-authorized user, send him back to course list...");
            response.sendRedirect(request.getContextPath() + "/course");
            return;
        }

        UserEntity user = (UserEntity) subject.getSession().getAttribute("user");
        if (user != null) {
            if (course.countMyCourses(user.getId()) < 1) {
                logger.debug("No course found... redirecting to course list.");
                response.sendRedirect(request.getContextPath() + "/course");
                return;
            }
        }

        TemplateRenderer renderer = new TemplateRenderer();
        DefaultLayoutTemplate template = new MyCoursesTemplate();
        template.setContextPath(request.getContextPath());
        template.setCurrentAction(CURRENT_ACTION);
        template.setCurrentController(CURRENT_CONTROLLER);


        if (!renderer.process(template, response.getWriter())) {
            try {
                request.getRequestDispatcher("/error.jspx").forward(request, response);
            } catch (ServletException e) {
                logger.error("loginAction() - Can't forward to error.jspx", e);
            }
        }

        logger.debug("mycoursesAction() - finished.");
    }

    /**
     * Enroll student to course.
     * <p/>
     * After success or not enroll action, flash message will be shown with result message.
     *
     * @param request  HttpServletRequest user request.
     * @param response HttpServletResponse server response.
     * @throws IOException Will thrown if can't get writer or this exception produced by
     *                     {@link HttpServletRequest#getRequestDispatcher(String)} method.
     */
    public void enrollAction(HttpServletRequest request, HttpServletResponse response) throws IOException {
        logger.debug("enrollAction() - started.");

        ResourceBundle i18n = LocaleWrapper.getBundle("course");
        String courseIdReq = request.getParameter("course_id");
        final Subject subject = SecurityUtils.getSubject();

        if (subject == null || !subject.isAuthenticated()) {
            logger.debug("Unauthorised session. Redirecting to course list.");
            response.sendRedirect(request.getContextPath() + "/");
            return;
        }

        UserEntity user = (UserEntity) subject.getSession().getAttribute("user");

        if (user == null) {
            logger.debug("Can't retrieve user entity. Redirecting to course list.");
            response.sendRedirect(request.getContextPath() + "/");
            return;
        }

        String studentId = studentId = user.getId();
        logger.debug("enrollAction() student ID {}", studentId);

        if (studentId == null || studentId.isEmpty() || courseIdReq == null || courseIdReq.isEmpty()) {
            logger.debug("enrollAction() No studentId or courseId sent. Redirecting to course list. " +
                    "Sent studentID: {}, course ID {}", studentId, courseIdReq);
            response.sendRedirect(request.getContextPath() + "/course");
            return;
        }

        logger.debug("enrollAction() Enrolling student with ID {} to course with ID {}", studentId, courseIdReq);
        String flashMessage = i18n.getString("enroll-message-fail");

        try {
            boolean isEnrolled = course.enrollStudentToCourse(studentId,
                    Long.valueOf(courseIdReq), getRequestURL(request));

            logger.debug("User is {} to course", ((isEnrolled) ? "enrolled" : "not enrolled"));

            if (isEnrolled) {
                flashMessage = i18n.getString("enroll-message-success");
            }
        } catch (UserEmailConfirmationException e) {
            flashMessage = i18n.getString("email_unconfirmed");
            logger.debug("Email unconfirmed.");
        } catch (StudentProfileInconsistencyException e) {
            flashMessage = i18n.getString("profile_inconsistent");
            logger.debug("Profile inconsistent.");
        } catch (StudentAlreadyEnrolledException e) {
            flashMessage = i18n.getString("student_already_enrolled");
            logger.debug("Student already enrolled.");
        } catch (StudentInPendingException e) {
            flashMessage = i18n.getString("student_still_pending");
            logger.debug("Student still pending.");
        }

        logger.debug("enrollAction() - finished.");
        subject.getSession().setAttribute(DefaultLayoutTemplate.FLASH_MESSAGE, flashMessage);
        response.sendRedirect(request.getContextPath() + "/");
        return;
    }

    /**
     * Un-enroll student from course.
     * <p/>
     * In case of success, flash message will be show with result.
     *
     * @param request  HttpServletRequest user request.
     * @param response HttpServletResponse server response.
     * @throws IOException Will thrown if can't get writer or this exception produced by
     *                     {@link HttpServletRequest#getRequestDispatcher(String)} method.
     */
    public void unenrollAction(HttpServletRequest request, HttpServletResponse response) throws IOException {
        logger.debug("unenrollAction() - started.");
        request.setCharacterEncoding("UTF-8");

        ResourceBundle i18n = LocaleWrapper.getBundle("course");
        String courseIdReq = request.getParameter("course_id");
        String reason = request.getParameter("reason");
        final Subject subject = SecurityUtils.getSubject();

        if (subject == null || !subject.isAuthenticated()) {
            logger.error("Subject is null or unauthorised user... Redirection to root");
            response.sendRedirect(request.getContextPath() + "/");
            return;
        }

        UserEntity user = (UserEntity) subject.getSession().getAttribute("user");

        if (user == null) {
            logger.error("Can't get User information... Redirecting to root.");
            response.sendRedirect(request.getContextPath() + "/");
            return;
        }

        String userId = user.getId();

        boolean isUnEnrolled = false;
        String flashMessage = i18n.getString("unenroll-message-fail");

        try {
            isUnEnrolled = course.unenrollStudentFromCourse(userId, Long.valueOf(courseIdReq), reason);
        } catch (StudentNotEnrolledException e) {
            logger.error("Student never enrolled for this course.");
        }

        logger.debug("User is {} to course", ((isUnEnrolled) ? "enrolled" : "not enrolled"));

        if (isUnEnrolled) {
            flashMessage = i18n.getString("unenroll-message-success");
        }
        subject.getSession().setAttribute(DefaultLayoutTemplate.FLASH_MESSAGE, flashMessage);
        response.sendRedirect(request.getContextPath() + "/");

        logger.debug("unenrollAction() - finished.");
    }

    /**
     * Approve student enrollment by MD5 hash.
     * @TODO: Should add method that add SVN credentials for user to course relation.!!!
     *
     * @param request  HttpServletRequest user request.
     * @param response HttpServletResponse server response.
     * @throws IOException Will thrown if can't get writer or this exception produced by
     *                     {@link HttpServletRequest#getRequestDispatcher(String)} method.
     */
    public void enrollApproveAction(HttpServletRequest request, HttpServletResponse response) throws IOException {
        logger.debug("enrollApproveAction() - started.");

        ResourceBundle i18n = LocaleWrapper.getBundle("course");
        String hash = request.getParameter("id");

        if (hash == null || hash.isEmpty()) {
            logger.error("Hash can't be {}", hash);
            logger.debug("Redirecting to /");
            response.sendRedirect(request.getContextPath() + "/");
            return;
        }
        Subject subject = SecurityUtils.getSubject();

        String flashMessage = i18n.getString("user_to_course_approving_fail");

        if (course.approveStudentEnrollment(hash, getRequestURL(request))) {
            flashMessage = i18n.getString("user_to_course_approving_success");
        }

        if (subject != null) {
            subject.getSession().setAttribute(DefaultLayoutTemplate.FLASH_MESSAGE, flashMessage);
        }
        logger.debug("enrollApproveAction() - finished.");
        response.sendRedirect(request.getContextPath() + "/");
    }

    /**
     * Action that discards student from being enrolled to course as a student.
     *
     * @param request  HttpServletRequest user request.
     * @param response HttpServletResponse server response.
     * @throws IOException Will thrown if can't get writer or this exception produced by
     *                     {@link HttpServletRequest#getRequestDispatcher(String)} method.
     */
    public void enrollDiscardAction(HttpServletRequest request, HttpServletResponse response) throws IOException {
        logger.debug("enrollDiscardAction() - started.");

        ResourceBundle i18n = LocaleWrapper.getBundle("course");
        String hash = request.getParameter("id");

        if (hash == null || hash.isEmpty()) {
            logger.error("Hash can't be {}", hash);
            logger.debug("Redirecting to /");
            response.sendRedirect(request.getContextPath() + "/");
            return;
        }

        Subject subject = SecurityUtils.getSubject();
        String flashMessage = i18n.getString("user_to_course_discard_fail");

        if (course.discardStudentEnrollment(hash, getRequestURL(request))) {
            flashMessage = i18n.getString("user_to_course_discard_success");
        }

        if (subject != null) {
            subject.getSession().setAttribute(DefaultLayoutTemplate.FLASH_MESSAGE, flashMessage);
        }

        logger.debug("enrollDiscardAction() - finished.");
        response.sendRedirect(request.getContextPath() + "/");
    }

    public void svnAction(HttpServletRequest request, HttpServletResponse response) throws IOException {
        logger.debug("avnAction() - started.");
        final String CURRENT_ACTION = "svn";
        Subject subject = SecurityUtils.getSubject();

        if (subject == null || !subject.isAuthenticated()) {
            logger.error("Subject is null or unauthorised user... Redirection to root");
            response.sendRedirect(request.getContextPath() + "/");
            return;
        }

        UserEntity user = (UserEntity) subject.getSession().getAttribute("user");

        if (user == null) {
            logger.error("Can't get User information... Redirecting to root.");
            response.sendRedirect(request.getContextPath() + "/");
            return;
        }


        String courseIdReq = request.getParameter("course_id");

        if (courseIdReq == null || courseIdReq.isEmpty()) {
            logger.error("Course ID can't be {}", courseIdReq);
            logger.debug("Redirecting to /");
            response.sendRedirect(request.getContextPath() + "/");
            return;
        }

        TemplateRenderer renderer = new TemplateRenderer();
        SVNTemplate template = new SVNTemplate(Long.valueOf(courseIdReq));
        template.setContextPath(request.getContextPath());
        template.setCurrentAction(CURRENT_ACTION);
        template.setCurrentController(CURRENT_CONTROLLER);


        if (!renderer.process(template, response.getWriter())) {
            try {
                request.getRequestDispatcher("/error.jspx").forward(request, response);
            } catch (ServletException e) {
                logger.error("loginAction() - Can't forward to error.jspx", e);
            }
        }


        logger.debug("avnAction() - finished.");
    }
}
